//
// CSPTask+PromiseLike.m
// CSPTaskPromise
//
// Copyright (c) 2014,2015 Hironori Ichimiya <hiron@hironytic.com>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#import "CSPTask+PromiseLike.h"
#import "CSPExecutor.h"

NSString *const CSPMTaskErrorDomain = @"CSPMTaskErrorDomain";
NSString *const CSPMUnderlyingExceptionKey = @"CSPMUnderlyingExceptionKey";

@implementation CSPTask (PromiseLike)

- (CSPTask *)thenWithExecutor:(CSPExecutor *)executor withBlock:(CSPMSuccessResultBlock)block {
    return [self continueWithExecutor:executor withBlock: ^id (CSPTask *task) {
        if ([task error] != nil || [task exception] != nil || [task isCancelled]) {
            return task;
        } else {
            return block(task.result);
        }
    }];
}

- (CSPTask *)catchWithExecutor:(CSPExecutor *)executor withBlock:(CSPMErrorResultBlock)block {
    return [self continueWithExecutor:executor withBlock: ^id (CSPTask *task) {
        if (task.error) {
            return block(task.error);
        } else if (task.exception) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            dict[CSPMUnderlyingExceptionKey] = task.exception;
            NSString *reason = task.exception.reason;
            if (reason != nil) {
                dict[NSLocalizedDescriptionKey] = reason;
            }
            return block([NSError errorWithDomain:CSPMTaskErrorDomain code:CSPMTaskErrorException userInfo:dict]);
        } else {
            return task;
        }
    }];
}

- (CSPTask *)finallyWithExecutor:(CSPExecutor *)executor withBlock:(CSPMFinallyBlock)block {
    return [self continueWithExecutor:executor withBlock: ^id (CSPTask *task) {
        CSPTask *resultTask = block();
        if (resultTask != nil) {
            return resultTask.then( ^id (CSPTask *task2) {
                return task;
            });
        } else {
            return task;
        }
    }];
}

- (CSPTask *(^)(CSPContinuationBlock))continueWith {
    return ^CSPTask *(CSPContinuationBlock block) {
        return [self continueWithExecutor:[CSPExecutor defaultExecutor] withBlock:block];
    };
}

- (CSPTask *(^)(CSPMSuccessResultBlock))then {
    return ^CSPTask *(CSPMSuccessResultBlock block) {
        return [self thenWithExecutor:[CSPExecutor defaultExecutor] withBlock:block];
    };
}

- (CSPTask *(^)(CSPMErrorResultBlock))catch {
    return ^CSPTask *(CSPMErrorResultBlock block) {
        return [self catchWithExecutor:[CSPExecutor defaultExecutor] withBlock:block];
    };
}

- (CSPTask *(^)(CSPMErrorResultBlock))catchWith {
    return [self catch];
}

- (CSPTask *(^)(CSPMFinallyBlock))finally {
    return ^CSPTask *(CSPMFinallyBlock block) {
        return [self finallyWithExecutor:[CSPExecutor defaultExecutor] withBlock:block];
    };
}

- (CSPTask *(^)(CSPExecutor *, CSPContinuationBlock))continueOn {
    return ^CSPTask *(CSPExecutor *executor, CSPContinuationBlock block) {
        return [self continueWithExecutor:executor withBlock:block];
    };
}

- (CSPTask *(^)(CSPExecutor *, CSPMSuccessResultBlock))thenOn {
    return ^CSPTask *(CSPExecutor *executor, CSPMSuccessResultBlock block) {
        return [self thenWithExecutor:executor withBlock:block];
    };
}

- (CSPTask *(^)(CSPExecutor *, CSPMErrorResultBlock))catchOn {
    return ^CSPTask *(CSPExecutor *executor, CSPMErrorResultBlock block) {
        return [self catchWithExecutor:executor withBlock:block];
    };
}

- (CSPTask *(^)(CSPExecutor *, CSPMFinallyBlock))finallyOn {
    return ^CSPTask *(CSPExecutor *executor, CSPMFinallyBlock block) {
        return [self finallyWithExecutor:executor withBlock:block];
    };
}

- (CSPTask *(^)(CSPContinuationBlock))continueOnMain {
    return ^CSPTask *(CSPContinuationBlock block) {
        return [self continueWithExecutor:[CSPExecutor mainThreadExecutor] withBlock:block];
    };
}

- (CSPTask *(^)(CSPMSuccessResultBlock))thenOnMain {
    return ^CSPTask *(CSPMSuccessResultBlock block) {
        return [self thenWithExecutor:[CSPExecutor mainThreadExecutor] withBlock:block];
    };
}

- (CSPTask *(^)(CSPMErrorResultBlock))catchOnMain {
    return ^CSPTask *(CSPMErrorResultBlock block) {
        return [self catchWithExecutor:[CSPExecutor mainThreadExecutor] withBlock:block];
    };
}

- (CSPTask *(^)(CSPMFinallyBlock))finallyOnMain {
    return ^CSPTask *(CSPMFinallyBlock block) {
        return [self finallyWithExecutor:[CSPExecutor mainThreadExecutor] withBlock:block];
    };
}

@end
